﻿using System.Collections.Generic;
using System.IO;

using NUnit.Framework;
using System.Net.DnsToolkit.Util;
using System.Net.DnsToolkit.Packet;
using System.Net.DnsToolkit.Packet.Records;
using System.Net.DnsToolkit.Serializer;
using System.Net.DnsToolkit.Serializer.Stream.Extension;
using System.Net.DnsToolkit.Serializer.Stream.Compression;
using System.Net.DnsToolkit.TestUnit.Helper;
using System.Net.DnsToolkit.Packet.Busy;
using System.Net.DnsToolkit.Packet.Busy.Records;

namespace System.Net.DnsToolkit.TestUnit.Serializer {
    [TestFixture]
    public class DnsSerializerTest {

        #region SerializeString
        [Test]
        public void TestSerializeString() {

            var bs = new MemoryStream();
            const string source = "string";
            const string expected = "06737472696E6700";

            bs.WriteDomainName(source);
            var stream = new MemoryStream();
            bs.WriteTo(stream);

            string actual = BitStringHelper.ByteArrayToString(stream.ToArray());
            Assert.AreEqual(expected, actual, "String");

        }
        [Test]
        public void TestSerializeString2() {

            var bs = new MemoryStream();
            const string source = "string.";
            const string expected = "06737472696E6700";

            bs.WriteDomainName(source);
            var stream = new MemoryStream();
            bs.WriteTo(stream);

            string actual = BitStringHelper.ByteArrayToString(stream.ToArray());
            Assert.AreEqual(expected, actual, "String");

        }



        #endregion

        #region DeserializeDomainName
        [Test]
        public void TestDeserializeDomainName1PointerLess() {

            const string source = "06 73 74 72 69 6E 67 00";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            var bs = new MemoryStream(ss);

            const string expected = "string.";
            string actual = bs.ReadDomainString();
            Assert.AreEqual(expected, actual, "String");
            Assert.AreEqual(8, bs.Position, "Position");

        }

        [Test]
        public void TestDeserializeDomainName2WithPointer() {

            const string source = "03 74 6C 64 00 06 64 6F 6D 61 69 6E C0 00 04 68 6F 73 74 C0 05";
            const string expected = "host.domain.tld.";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            
            var bs = new MemoryStream(ss) {Position = 14};
            string actual = bs.ReadDomainString();

            Assert.AreEqual(expected, actual, "String");
            Assert.AreEqual(21, bs.Position, "Position");

        }

        [Test]
        public void TestDeserializeDomainName3LoopPointer() {

            const string expected = "host.domain.tld.host.";
            const string source = "03 74 6C 64 C0 0F 06 64 6F 6D 61 69 6E C0 00 04 68 6F 73 74 C0 06";
            byte[] ss = BitStringHelper.StringToByteArray(source);

            var bs = new MemoryStream(ss) {Position = 15};
            string actual = bs.ReadDomainString();

            Assert.AreEqual(expected, actual);

        }


        [Test]
        public void TestDeserializeDomainName5OutOfBoundPointer() {

            const string expected = "host.domain.tld.";
            const string source = "03 74 6C 64 FF FF 06 64 6F 6D 61 69 6E C0 00 04 68 6F 73 74 C0 06";
            byte[] ss = BitStringHelper.StringToByteArray(source);

            var bs = new MemoryStream(ss) {Position = 15};
            string actual = bs.ReadDomainString();
            Assert.AreEqual(expected, actual);

        }


        [Test]
        public void TestDeserializeDomainName6TooLong() {

            const string expected = "1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234.domn.tl.";
            const string source = "0a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a313233343536373839300a31323334353637383930043132333404646f6d6e02746c0000020001";
            byte[] ss = BitStringHelper.StringToByteArray(source);
            var bs = new MemoryStream(ss);

            string actual = bs.ReadDomainString();

            Assert.AreEqual(expected, actual);

        }

        
        #endregion

        #region Compression

        static IMessage CompressionMessage() {


            var message = new Message {
                Header = new Header {
                    Id = 3,
                    // flags
                    QueryType = QREnum.QUERY,
                    OpCode = OpCodeEnum.StandardQuery,
                    AuthoritativeAnswer = false,
                    Truncation = false,
                    RecursionDesired = true,
                    RecursionAvailable = false,
                    Reserved = false,
                    AnswerAuthenticated = false,
                    NotAuthenticatedAcceptable = false,
                    ResponseCode = RCodeEnum.NoError,
                    // counts
                    QuestionCount = 1,
                    ResourceCount = 0,
                    AnswerCount = 0,
                    AuthorityCount = 0,
                },
                Questions = new List<IQuestion> {
                    new Question {
                        QName = new DomainName {
                            Name = "www.domn.tl."
                        },
                        QType = QTypeEnum.A,
                        QClass = QClassEnum.IN
                    }
                },
                Answers = new List<IRR> {
                    new RR {
                        Name = new DomainName
                        {Name = "www.domn.tl."},
                        Ttl = 0,
                        RRClass = QClassEnum.IN,
                        RRType = QTypeEnum.A,
                        RDLength = 4,
                        RData = new RecordA {
                            Address =IPAddress.Parse("1.2.3.4")
                        }
                    }
                }
            };

            return message;
        }

        [Test]
        public void TestCompressionNo() {


            var message = CompressionMessage();
            const string expected = "0003010000010000000000000377777704646F6D6E02746C00000100010377777704646F6D6E02746C000001000100000000000401020304";

            var stream = new MemoryStream();
            DnsWriter.Write(stream, message, false);

            byte[] bs = stream.ToArray();
            string actual = BitStringHelper.ByteArrayToString(bs);
            Assert.AreEqual(expected, actual, "message");

        }

        [Test]
        public void TestCompression1() {

            var message = CompressionMessage();
            const string expected = "0003010000010000000000000377777704646F6D6E02746C0000010001C00C0001000100000000000401020304";
            
            var stream = new MemoryStream();
            DnsWriter.Write(stream, message, true);

            byte[] bs = stream.ToArray();
            string actual = BitStringHelper.ByteArrayToString(bs);
            Assert.AreEqual(expected, actual, "message");

        }

        #endregion

    }
}